package cn.iocoder.yudao.module.system.tcpserver.protocol2;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.dal.dataobject.zkdata.ZkDataDO;
import cn.iocoder.yudao.module.system.dal.mysql.zkdata.ZkDataMapper;
import cn.iocoder.yudao.module.system.tcpserver.pojo.HeartInfo;
import cn.iocoder.yudao.module.system.tcpserver.pojo.NewProtocolConstant;
import cn.iocoder.yudao.module.system.tcpserver.pojo.ZkPacketFrame;
import cn.iocoder.yudao.module.system.tcpserver.utils.ZkCrcUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class NewZkDataProcessService {

    public static final Map<String, HeartInfo> clientMap = new ConcurrentHashMap<>();

    @Resource
    private ZkDataMapper zkDataMapper;

    public void sendData(String deviceNo, Channel channel) {
        List<ZkDataDO> dataToSend = getDataToSend(deviceNo);
        if (CollUtil.isNotEmpty(dataToSend)) {
            dataToSend.forEach(data -> {
                channel.writeAndFlush(new ZkPacketFrame(HexUtil.decodeHex(data.getOriData())));
                updateStatus(data);
            });
        }
    }

    public List<ZkDataDO> getDataToSend(String receiver) {
        List<ZkDataDO> zkDataDOS = zkDataMapper.selectList(ZkDataDO::getDataReceiver, receiver, ZkDataDO::getStatus, 0);
        return zkDataDOS;
    }

    public void updateStatus(ZkDataDO zkDataDO) {
        zkDataDO.setStatus(1);
        zkDataMapper.updateById(zkDataDO);
    }

    public ZkPacketFrame process(ZkDataDO msg) {

        if (msg.getDataType() == NewProtocolConstant.HEART) {
            //心跳数据
            if (clientMap.containsKey(msg.getDataSender())) {
                //已上报过心跳
                LocalDateTime now = LocalDateTime.now();
                HeartInfo heartInfo = clientMap.get(msg.getDataSender());
                heartInfo.setDateTime(now);
                if (heartInfo.getFirstTime() == null) {
                    log.info("NNNN 已上报过心跳，但中间断开了:{}", msg.getDataSender());
                    //已上报过心跳，但中间断开了
                    heartInfo.setFirstTime(now);
                    //第一次连接，发送请求上行指令
                    return new ZkPacketFrame(NewProtocolConstant.UPLOAD_REQ, true);
                } else {
                    //已上报过心跳，且未断开过
                    return new ZkPacketFrame(null);
                }

            } else {
                log.info("NNNN 第一次心跳:{}", msg.getDataSender());
                //第一次上报心跳
                LocalDateTime now = LocalDateTime.now();
                HeartInfo build = HeartInfo.builder()
                        .dateTime(now).firstTime(now).sender(msg.getDataSender()).build();
                clientMap.put(msg.getDataSender(), build);
                //第一次连接，发送请求上行指令
                return new ZkPacketFrame(NewProtocolConstant.UPLOAD_REQ, true);
            }
        } else if (msg.getDataType() == NewProtocolConstant.LOCAL_TYPE.UPLOAD_REQ_ACK) {
            //请求上行 ACK 啥也不用做，等待 上行数据开始
            //流程修改 当数据包长为0的时候，说明 没有数据，平台向浮标发送下行请求。。
            if (msg.getPacketLength() == 0) {
                //请求上行响应中，需要上传的数据不存在时，向浮标发送下行请求
                return getSendData(msg);
            } else {
                return new ZkPacketFrame(null);
            }
        } else if (msg.getDataType() == NewProtocolConstant.LOCAL_TYPE.UPLOAD_DATA_BEGIN) {
            //上行数据开始 回复 ACK

//            数传开始包
//            EB 90 00 0C 01 A1 00 00 00 02 7B 58
//            数传开始包ACK
//            EB 90 00 0C 01 A5 00 00 00 00 7A 28
            String oriData = msg.getOriData();
            byte[] bytes = HexUtil.decodeHex(oriData);
            int length = bytes.length;
            bytes[5] = (byte) NewProtocolConstant.DATA_ACK;
            bytes[6] = 0x00;
            bytes[7] = 0x00;
            bytes[8] = 0x00;
            bytes[9] = 0x00;
            byte[] crc = ZkCrcUtils.getCrc(bytes);
            bytes[length - 2] = crc[0];
            bytes[length - 1] = crc[1];
            return new ZkPacketFrame(bytes);

        } else if (msg.getDataType() == NewProtocolConstant.LOCAL_TYPE.UPLOAD_DATA) {
            //业务数据 回复ACK
//            第一包数据例如
//            EB 90 00 13 01 A1 00 01 01 02 03 04 05 06 07 08 09 0F 19
//            第一包数据ACK
//            EB 90 00 0C 01 A5 00 01 00 01 7A B8
            String oriData = msg.getOriData();
            byte[] bytes = HexUtil.decodeHex(oriData);
            byte[] uploadDataAck = NewProtocolConstant.UPLOAD_DATA_ACK;
            uploadDataAck[4] = bytes[4]; //设备码 01 02
            uploadDataAck[6] = bytes[6];
            uploadDataAck[7] = bytes[7];
            uploadDataAck[8] = bytes[6];
            uploadDataAck[9] = bytes[7];
            byte[] crc = ZkCrcUtils.getCrc(uploadDataAck);
            uploadDataAck[uploadDataAck.length - 2] = crc[0];
            uploadDataAck[uploadDataAck.length - 1] = crc[1];
            //todo 存储 业务数据
            if (uploadDataAck[4] == NewProtocolConstant.DEVICE01) {
                msg.setDataSender(NewProtocolConstant.DEVICE01NO);
            } else if (uploadDataAck[4] == NewProtocolConstant.DEVICE02) {
                msg.setDataSender(NewProtocolConstant.DEVICE02NO);
            }
            msg.setDataReceiver(NewProtocolConstant.DEVICE0NO);

            zkDataMapper.insert(msg);
            return new ZkPacketFrame(uploadDataAck);
        } else if (msg.getDataType() == NewProtocolConstant.LOCAL_TYPE.UPLOAD_END) {
//            收到数传结束包
//            EB 90 00 0C 01 A4 FF FF 00 02 5F 94
//            回复数传结束包的ACK
//            EB 90 00 0C 01 A5 FF FF 00 02 9F A9
            String oriData = msg.getOriData();
            byte[] bytes = HexUtil.decodeHex(oriData);
            byte[] uploadDataAck = NewProtocolConstant.UPLOAD_DATA_END_ACK;
            uploadDataAck[4] = bytes[4]; //设备码 01 02
            uploadDataAck[6] = bytes[6];
            uploadDataAck[7] = bytes[7];
            uploadDataAck[8] = bytes[8];
            uploadDataAck[9] = bytes[9];
            byte[] crc = ZkCrcUtils.getCrc(uploadDataAck);
            uploadDataAck[uploadDataAck.length - 2] = crc[0];
            uploadDataAck[uploadDataAck.length - 1] = crc[1];
            return new ZkPacketFrame(uploadDataAck, false, true);
        }
        //下行
        else if (msg.getDataType() == NewProtocolConstant.LOCAL_TYPE.DOWNLOAD_REQ_ACK) {
            //下行ACK 发送数据开始包
            List<ZkDataDO> dataToSend = getDataToSend(msg.getDataSender());
            byte[] downloadBeginData = NewProtocolConstant.DOWNLOAD_BEGIN_DATA;

            byte highByte = (byte) ((dataToSend.size() >> 8) & 0xFF); // 高字节
            byte lowByte = (byte) (dataToSend.size() & 0xFF);         // 低字节
            downloadBeginData[8] = highByte;
            downloadBeginData[9] = lowByte;
            byte[] crc = ZkCrcUtils.getCrc(downloadBeginData);
            downloadBeginData[10] = crc[0];
            downloadBeginData[11] = crc[1];
            return new ZkPacketFrame(downloadBeginData);
        } else if (msg.getDataType() == NewProtocolConstant.LOCAL_TYPE.DOWNLOAD_REQ) {
            return new ZkPacketFrame(NewProtocolConstant.DOWNLOAD_REQ_ACK);
        } else if (msg.getDataType() == NewProtocolConstant.LOCAL_TYPE.DOWNLOAD_DATA) {
            Integer dataSeq = msg.getDataSeq();
            String dataReceiver = null;
            if (Objects.equals(msg.getDataSender(), "1")) {
                dataReceiver = NewProtocolConstant.DEVICE01NO;
            } else if (Objects.equals(msg.getDataSender(), "2")) {
                dataReceiver = NewProtocolConstant.DEVICE02NO;
            } else {
                return new ZkPacketFrame(null);
            }

            if (dataSeq > 0) {
                LambdaQueryWrapperX<ZkDataDO> updateWrapper = new LambdaQueryWrapperX<>();
                updateWrapper.eq(ZkDataDO::getDataReceiver, dataReceiver);
                updateWrapper.eq(ZkDataDO::getDataSeq, dataSeq);
                updateWrapper.eq(ZkDataDO::getStatus, 0);
                ZkDataDO ackZkData = zkDataMapper.selectOne(updateWrapper);

                if (ackZkData != null) {
                    updateStatus(ackZkData);
                }
            }

            LambdaQueryWrapperX<ZkDataDO> wrapper = new LambdaQueryWrapperX<>();
            wrapper.eq(ZkDataDO::getDataReceiver, dataReceiver);
            wrapper.eq(ZkDataDO::getDataSeq, dataSeq + 1);
            wrapper.eq(ZkDataDO::getStatus, 0);
            ZkDataDO zkDataDO = zkDataMapper.selectOne(wrapper);
            if (zkDataDO != null) {
                return new ZkPacketFrame(HexUtil.decodeHex(zkDataDO.getOriData()));
            } else {
                //数据发送完毕
                byte[] downloadEnd = NewProtocolConstant.DOWNLOAD_END;
                byte highByte = (byte) ((dataSeq >> 8) & 0xFF); // 高字节
                byte lowByte = (byte) (dataSeq & 0xFF);         // 低字节
                downloadEnd[8] = highByte;
                downloadEnd[9] = lowByte;
                byte[] crc = ZkCrcUtils.getCrc(downloadEnd);
                downloadEnd[10] = crc[0];
                downloadEnd[11] = crc[1];
                return new ZkPacketFrame(downloadEnd);
            }

        } else {
            log.warn("NNNN 未知数据:{}", JSONUtil.toJsonStr(msg));
            return new ZkPacketFrame(null);
        }
    }

    public ZkPacketFrame getSendData(ZkDataDO msg) {
        if (msg.getDataSender() != null) {
            List<ZkDataDO> dataToSend = getDataToSend(msg.getDataSender());
            byte[] downloadReq = NewProtocolConstant.DOWNLOAD_REQ;

            byte highByte = (byte) ((dataToSend.size() >> 8) & 0xFF); // 高字节
            byte lowByte = (byte) (dataToSend.size() & 0xFF);         // 低字节
            downloadReq[6] = highByte;
            downloadReq[7] = lowByte;
            byte[] crc = ZkCrcUtils.getCrc(downloadReq);
            downloadReq[8] = crc[0];
            downloadReq[9] = crc[1];
            return new ZkPacketFrame(downloadReq);
        }
        return new ZkPacketFrame(null);
    }

    public void sendServerHeartbeat(Channel channel, String sender) {
        HeartInfo heartInfo = clientMap.get(sender);
        if (LocalDateTimeUtil.between(heartInfo.getDateTime(), LocalDateTime.now(), ChronoUnit.SECONDS) > 60000) {
            clientOffLine(sender);
        }

        if (LocalDateTimeUtil.between(heartInfo.getDateTime(), LocalDateTime.now(), ChronoUnit.SECONDS) > 3000000) {
            log.info("NNNN 超时断开:{}", sender);
            cleanupClient(sender);
        } else {
            if (channel.isActive()) {
                if (clientMap.containsKey(sender)) {
                    // 根据clientId生成自定义内容（示例逻辑）
//                    log.info("sender:{}-heartbeat", sender);
//                    channel.writeAndFlush(new ZkPacketFrame(heartInfo.getData()));
                } else {
                    log.warn("NNNN sender:{} not exist", sender);
                }

            } else {
                // 清理无效连接
                cleanupClient(sender);
            }
        }

    }

    public void clientOffLine(String sender) {
        if (clientMap.containsKey(sender)) {
            clientMap.get(sender).setFirstTime(null);
        }
    }

    private void cleanupClient(String clientId) {
        clientOffLine(clientId);
        NewHeartbeatServer.ChannelSchedule schedule = NewHeartbeatServer.getClientMap().remove(clientId);
        if (schedule != null && schedule.future != null) {
            schedule.future.cancel(true);
            log.info("NNNN 心跳任务关闭：{}", clientId);
        }
        if (schedule != null && schedule.channel != null) {
            schedule.channel.close();
            log.info("NNNN 连接关闭：{}", clientId);
        }
    }

    public byte[] heartProcess(ZkDataDO msg) {

        String oriData = msg.getOriData();
        byte[] bytes = HexUtil.decodeHex(oriData);
        int length = bytes.length;
        byte[] oriSender = HexUtil.decodeHex(msg.getDataSender());
        bytes[4] = oriSender[0];
        bytes[5] = oriSender[1];
        bytes[6] = (byte) 0xFF;
        bytes[7] = (byte) 0xFF;
        byte[] crc = ZkCrcUtils.getCrc(bytes);
        bytes[length - 2] = crc[0];
        bytes[length - 1] = crc[1];
        return bytes;
    }


}
